﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Nemerle.Xml
{
  using Node;
  using DocumentElement;
  using Namespace;

  /// <summary>
  /// Describes Document or PI.
  /// </summary>
  public variant DocumentElement : ElementBase
  {
    | Document { instrs : list[ProcInstruction]; nodes : list[Node]; }
    | ProcInstruction { name : string; value : string; }

    public override Transform(transform : ElementBase -> ElementBase) : ElementBase
    {
      match (this)
      {
        | Document(procInstrs, nodes) =>
          def newNodes = Utils.TransformList(nodes, transform);
          def newProcInstrs = Utils.TransformList(procInstrs, transform);
          if (newNodes.TryForAll2(nodes, _refCmpFunc)
              && newProcInstrs.TryForAll2(procInstrs, _refCmpFunc))
            this
          else
            Document(newProcInstrs, newNodes)

        | _ => transform(this)
      }
    }

    public override GetHashCode() : int
    {
      match (this)
      {
        | ProcInstruction(name, value) => name.GetHashCode() ^ value.GetHashCode()
        | Document([], []) => 928
        | Document([], nodes) => nodes.GetHashCode()
        | Document(instrs, []) => instrs.GetHashCode()
        | Document(instrs, nodes) => nodes.GetHashCode() ^ instrs.GetHashCode()
      }
    }

    public override Equals(other : ElementBase) : bool
    {
      | node is DocumentElement => Equals(node);
      | _ => false
    }

    public Equals(other : DocumentElement) : bool
    {
      if (object.ReferenceEquals(this, other))
        true
      else
        match (this, other)
        {
          | (Document(leftPi, leftNodes), Document(rightPi, rightNodes)) =>
              leftPi.TryForAll2(rightPi, (x, y) => x.Equals(y))
              && leftNodes.SkipSpaces().TryForAll2(rightNodes.SkipSpaces(), (x, y) => x.Equals(y))

          | (ProcInstruction(name, value), ProcInstruction(name1, value1)) =>
              name == name1 && value == value1

          | _ => false
        }
    }
  }
}
